package com.dchat.websocket.component;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.dchat.domain.Member;
import com.dchat.domain.User;
import com.dchat.service.inter.FriendService;
import com.dchat.service.inter.GroupService;
import com.dchat.service.inter.MemberService;
import com.dchat.service.inter.UserService;
import com.dchat.util.MyDateUtil;
import com.dchat.util.RedisOperator;
import com.dchat.websocket.domain.LobbyMessages;
import com.dchat.websocket.domain.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint("/dchat/{id}/{device}")
@Component
public class SocketServer {

    ///用来保存,用户的session
    public static Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    private static RedisOperator redisOperator;
    private static FriendService friendService;
    private static UserService userService;
    private static MemberService memberService;
    private static GroupService groupService;

    @Autowired
    public void setRedisOperator(RedisOperator redisOperator){
        SocketServer.redisOperator = redisOperator;
    }
    @Autowired
    public void setFriendService(FriendService friendService){
        SocketServer.friendService = friendService;
    }
    @Autowired
    public void setUserService(UserService userService){
        SocketServer.userService = userService;
    }
    @Autowired
    public void setMemberService(MemberService memberService){
        SocketServer.memberService = memberService;
    }
    @Autowired
    public void setGroupService(GroupService groupService){
        SocketServer.groupService = groupService;
    }





    @OnOpen
    public void onOpen(Session session, @PathParam("id")String id,@PathParam("device") String device){
        //先检查这个用户是不是注册用户
         if(null==userService.getUserById(Integer.valueOf(id))){
             this.sendMessage( JSONUtil.toJsonStr(new Message<>(400,"rejected","不存在该用户")),session);
             return;
         }

         //日志
        System.out.println("用户："+id+"上线了\t"+MyDateUtil.simpleDateFormat.format(new Date())+"\t使用设备:"+device);

        Session cacheSession = sessionMap.get(id);
        if(cacheSession==null){
            //不存在session则说明用户没有登录过   缓存用户的session
            sessionMap.put(id,session);
        }
        else{
            //APP设备上
            if(device.equals("APP")){
                //分别给原来的和现在呢的session发一个check
//                String message = JSONUtil.toJsonStr(new Message<>(200,"check",""));
//                this.sendMessage(message,session);
//                this.sendMessage(message,cacheSession);
                    //this.sendMessage(JSONUtil.toJsonStr(new Message<>(300, "kicked", "you are ticked")), cacheSession);
                    sessionMap.put(id,cacheSession);

            }else{
                if(!Objects.equals(cacheSession.getId(), session.getId())) {
                    //用户在其他的设备上登录   挤下线
                    //todo
                    //this.sendMessage(JSONUtil.toJsonStr(new Message<>(300, "kicked", "you are ticked")), cacheSession);
                    //覆盖原来的session
                    sessionMap.put(id, cacheSession);
                }
            }
        }

        ///将缓存消息推给用户
        List<Object> messages = redisOperator.getCacheMessages(id);
        if(null!=messages){
            for(Object message:messages){
                this.sendMessage((String) message,session);
            }
        }

        //公共聊天室消息
        List<Object> lobbyMessages =  redisOperator.getCacheLobbyMessages();

        this.sendMessage(JSONUtil.toJsonStr(new LobbyMessages<>("lobby-messages",lobbyMessages)),session);

        //在线人数
        this.sendAllMessage(JSONUtil.toJsonStr(new Message<>(200,"online-num",sessionMap.size())));
        System.out.println("当前在线人数："+sessionMap.size());
        System.out.println(sessionMap.keySet()+"\n");
    }
    @OnClose
    public void onClose(Session session,@PathParam("id") String id){
        Session cacheSession = sessionMap.get(id);
        if(ObjectUtils.isEmpty(cacheSession)){
            System.out.println("用户："+id+"正常下线\t"+MyDateUtil.simpleDateFormat.format(new Date()));
            System.out.println("当前在线人数："+sessionMap.size());
            System.out.println(sessionMap.keySet()+"\n");
            return;
        }
        try {
            cacheSession.close();
            sessionMap.remove(id);
        } catch (IOException e) {
            System.out.println("关闭连接出现错误");
        }

        //在线人数
        this.sendAllMessage(JSONUtil.toJsonStr(new Message<>(200,"online-num",sessionMap.size())));

        System.out.println("用户："+id+"正常下线\t"+MyDateUtil.simpleDateFormat.format(new Date()));
        System.out.println("当前在线人数："+sessionMap.size());
        System.out.println(sessionMap.keySet()+"\n");
    }
    @OnError
    public void onError(Session session,Throwable error,@PathParam("id") String id){
        Session cacheSession = sessionMap.get(id);
        if(ObjectUtils.isEmpty(cacheSession)){
            System.out.println("用户："+id+"异常下线\t"+MyDateUtil.simpleDateFormat.format(new Date()));
            System.out.println("当前在线人数："+sessionMap.size());
            System.out.println(sessionMap.keySet()+"\n");
            return;
        }
        try {
            cacheSession.close();
            sessionMap.remove(id,cacheSession);
        } catch (IOException e) {
            System.out.println("关闭连接出现错误");
        }

        //error.printStackTrace();

        System.out.println("用户："+id+"异常下线\t"+MyDateUtil.simpleDateFormat.format(new Date()));
        System.out.println("当前在线人数："+sessionMap.size());
        System.out.println(sessionMap.keySet()+"\n");
    }
    @OnMessage
    public void onMessage(String message,Session session,@PathParam("id") String id){
        JSONObject data = JSONUtil.parseObj(message);
        switch (data.getStr("type")){
            case "ping":
                Message<String> response = new Message<>(200,"pong","success");
                this.sendMessage(JSONUtil.toJsonStr(response),session);
                break;
            case "person_message":
                String toId = data.getStr("to");
                this.sendMessageById(toId,message);
                break;
            case "apply-person":
                this.applyPerson(data,session);
                break;
            case "apply-person-agree":
                this.applyPersonAgree(data,session);
                break;
            case "delete-person":
                this.deletePerson(data,session);
                break;
            case "lobby-message":
                this.lobbyMessage(message,session);
                break;
            case "group_message":
                this.groupMessage(message,session);
                break;
            case "apply-group":
                this.applyGroup(data,session);
                break;
            case "apply-group-agree":
                this.applyGroupAgree(data,session);
                break;
            case "quit-group":
                this.quitGroup(data,session);
                break;
            case "kick-person":
                this.kickPerson(data,session);
                break;
            case "invite-people":
                this.invitePeople(data);
                break;
        }
    }


    //邀请好友加入群聊
    private void invitePeople(JSONObject message){
        String[] ids = message.getStr("ids").split(",");
        for(String id:ids){
            this.sendMessageById(id,JSONUtil.toJsonStr(message));
        }
    }
    //踢人
    private void kickPerson(JSONObject message,Session session){
        JSONObject group = JSONUtil.parseObj(message.getStr("group"));
        String groupId = group.getStr("id");
        memberService.deleteOne(groupId,Integer.valueOf(message.getStr("to")));

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("type","quit-group");
        jsonObject.put("id",groupId);
        jsonObject.put("content","你已被踢出群聊："+group.getStr("name"));
        this.sendMessageById(message.getStr("to"),JSONUtil.toJsonStr(jsonObject));
        this.sendMessage(JSONUtil.toJsonStr(new Message<>(200,"success-message","操作成功！")),session);
    }
    //处理退群消息
    private void quitGroup(JSONObject message,Session session){
        //先判断一下用户是不是群主
        JSONObject group = JSONUtil.parseObj(message.getStr("group"));
        String groupId = group.getStr("id");
        if(message.getStr("from").equals(group.getStr("ownerId"))){
            //通知所有人群已经解散了
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("type","quit-group");
            jsonObject.put("id",groupId);
            jsonObject.put("to",groupId);
            jsonObject.put("content","群聊:"+group.getStr("name")+"已解散");
            this.groupMessage(JSONUtil.toJsonStr(jsonObject),session);

            groupService.deleteOne(groupId);
            //清除群id
            memberService.clearAll(groupId);
        }else{
            memberService.deleteOne(groupId,Integer.valueOf(message.getStr("from")));
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("type","quit-group");
            jsonObject.put("id",groupId);
            jsonObject.put("content","成功退群");
            this.sendMessage(JSONUtil.toJsonStr(jsonObject),session);
        }
    }

    //处理群消息
    private void groupMessage(String message,Session session){
        JSONObject from = JSONUtil.parseObj(message);
        //获取群的缓存id发送给所有群用户
        List ids = (List)redisOperator.getCacheMemberId(from.getStr("to"));
        for(Object id:ids){
            this.sendMessageById(id.toString(),message);
        }
    }


    //处理删除请求
    private void lobbyMessage(String message,Session session){
        redisOperator.cacheLobbyMessage(JSONUtil.parseObj(message).getStr("message"));
        this.sendAllMessage(message);
    }

    //处理删除请求
    private void deletePerson(JSONObject message,Session session){
        String fromId = message.getStr("from");
        String toId = message.getStr("to");
        friendService.deleteFriend(fromId,toId);
        this.sendMessageById(toId,JSONUtil.toJsonStr(message));
    }


    //处理好友同意请求
    private void applyPersonAgree(JSONObject message,Session session){
        JSONObject from = JSONUtil.parseObj(message.getStr("from"));
        String fromId = from.getStr("id");
        String toId = message.getStr("to");
        Boolean isFriend = friendService.isFriend(fromId,toId);
        if(!isFriend){
           friendService.addFriend(fromId,toId);
           //转发给申请人
            this.sendMessageById(toId,JSONUtil.toJsonStr(message));
        }
    }
    //处理好友同意请求
    private void applyGroupAgree(JSONObject message,Session session){
        String toId = message.getStr("to");
        JSONObject group = JSONUtil.parseObj(message.getStr("group"));
        Member member = memberService.isMember(group.getStr("id"),Integer.valueOf(toId));
        if(null == member){
            memberService.insertOne(new Member(null,group.getStr("id"),Integer.valueOf(toId), MyDateUtil.simpleDateFormat.format(new Date())));
            this.sendMessageById(toId,JSONUtil.toJsonStr(message));
            return;
        }
        this.sendMessage(JSONUtil.toJsonStr(new Message<>(200,"success-message","操作成功啦")),session);
    }
    //处理入群
    private void applyGroup(JSONObject message,Session session){
        JSONObject from = JSONUtil.parseObj(message.getStr("from"));
        String fromId = from.getStr("id");
        String toId = message.getStr("to");
        JSONObject group = JSONUtil.parseObj(message.getStr("group"));
       ///先检查自己是不是在群里面
        Member member = memberService.isMember(group.getStr("id"),Integer.valueOf(fromId));

        if(null != member){
            this.sendMessage(JSONUtil.toJsonStr(new Message<>(300,"apply-person-fail","你已经在这个群里啦")),session);
            return;
        }
        this.sendMessageById(toId,JSONUtil.toJsonStr(message));
        this.sendMessage(JSONUtil.toJsonStr(new Message<>(200,"success-message","发送请求成功啦")),session);
    }

    //处理加好友申请
    private void applyPerson(JSONObject message,Session session){
        JSONObject from = JSONUtil.parseObj(message.getStr("from"));
        String fromId = from.getStr("id");
        String toId = message.getStr("to");
        if(Objects.equals(fromId,toId)){
            this.sendMessage(JSONUtil.toJsonStr(new Message<>(301,"apply-person-fail","不允许加自己为好友哦")),session);
            return;
        }
        Boolean isFriend = friendService.isFriend(fromId,toId);
        if(isFriend){
            //是好友
            this.sendMessage(JSONUtil.toJsonStr(new Message<>(300,"apply-person-fail","TA已经是你好友了")),session);
            return;
        }
        //不是好友的话 就发给对方好友申请
        this.sendMessageById(toId,JSONUtil.toJsonStr(message));
        this.sendMessage(JSONUtil.toJsonStr(new Message<>(200,"success-message","发送请求成功啦")),session);
    }

    private void sendMessageById(String id,String message){
        for(String key:sessionMap.keySet()){
            if(Objects.equals(key, id)){
                this.sendMessage(message,sessionMap.get(key));
                return;
            }
        }
        //如果收信人不在线，则将信息进行缓存
        redisOperator.cacheMessages(id,message);
    }

    private void sendMessage(String message,Session toSession){
        try {
            if(toSession.isOpen())
                toSession.getBasicRemote().sendText(message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void sendAllMessage(String message){
        try {
            for(Session session : sessionMap.values()){
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(message);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
